import { shapeFlags } from '../shared/ShapeFlags'
import { createComponentInstance, setupComponent } from "./component"
import { createAppAPI } from './createApp';
import { Fragment,Text } from './vnode';
import { effect } from '../reactivity/effect';
import { EMPTY_OBJ } from '../shared';
import { shouldUpdateComponent } from './componentUpdateUtils';
import { queueJobs } from './scheduler';

export function createRenderer(options) {

    const { 
        createElement:hostCreateElement,
        patchProp:hostPatchProp,
        insert:hostInsert, 
        remove:hostRemove, 
        setElementText:hostSetElementText, 
    } = options

    function render(vnode,container) {
        return patch(null,vnode,container,null,null)
    }

    // n1 -> 老的
    // n2 -> 新的
    function patch(n1,n2,container,parentComponent,anchor) {
        // 元素
        // if(typeof vnode.type === 'string') {
        const { type,shapeFlag } = n2
        // Fragment -> 只渲染children
        switch (type) {
            case Fragment:
                processFragment(n1,n2,container,parentComponent,anchor)
                break;
                
            case Text:
                processTextVNode(n1,n2,container)
                break;
        
            default:
                if(shapeFlag & shapeFlags.ELEMENT) {
                    processElement(n1,n2,container,parentComponent,anchor)
                } else if(shapeFlag & shapeFlags.STATEFUL_COMPONENT){
                    // 组件
                    processComponent(n1,n2,container,parentComponent,anchor)
                }
                break;
        }  
    }

    function processFragment(n1,n2: any, container: any,parentComponent,anchor) {
        mountChildren(n2.children,container,parentComponent,anchor)
    }
    function processTextVNode(n1,n2: any, container: any) {
        const children = n2.children
        const text = document.createTextNode(children)
        container.append(text)
    }
    function processElement(n1,n2: any, container: any,parentComponent,anchor) {
        if(!n1) {
            mountElement(n2,container,parentComponent,anchor)
        } else {
            patchElement(n1,n2,container,parentComponent,anchor)
        }
    }

    function patchElement(n1,n2,container,parentComponent,anchor) {
        // props
        const oldProps = n1.props || EMPTY_OBJ
        const newProps = n2.props || EMPTY_OBJ

        const el = (n2.el = n1.el)

        patchProps(el,oldProps,newProps)

        patchChildren(n1,n2,el,parentComponent,anchor)
    }

    function patchProps(el,oldProps,newProps) {
        if(oldProps !== newProps) {
            for (const key in newProps) {
                const prevProp = oldProps[key]
                const nextProp = newProps[key]
                if(prevProp !== nextProp) {
                    hostPatchProp(el,key,prevProp,nextProp)
                }
            }
            if(oldProps !== EMPTY_OBJ) {
                for (const key in oldProps) {
                    if(!(key in newProps)) {
                        hostPatchProp(el,key,oldProps[key],null)
                    }
                }
            }
        }
    }

    function patchChildren(n1,n2,container,parentComponent,anchor) {
        const prevShapeFlag = n1.shapeFlag
        const shapeFlag = n2.shapeFlag
        const c1 = n1.children
        const c2 = n2.children

        if(shapeFlag & shapeFlags.TEXT_CHILDREN) {
            if(prevShapeFlag & shapeFlags.ARRAY_CHILDREN) {
                // 1.把老的children清空
                unmountChildren(c1)
            } 
            if(c1 !== c2) {
                // 2.设置text
                hostSetElementText(container,c2)
            }
        } else{
            if(prevShapeFlag & shapeFlags.TEXT_CHILDREN) {
                // 1.把老的children清空
                hostSetElementText(container,'')
                // 2.将array遍历插入节点
                mountChildren(c2,container,parentComponent,anchor)
            } else {
                // array diff array
                patchKeyedChildren(c1,c2,container,parentComponent,anchor)
            }
        }
    }

    function patchKeyedChildren(c1,c2,container,parentComponent,parentAnchor) {
        let i = 0;
        let e1 = c1.length - 1
        let e2 = c2.length - 1 

        function isSomeVNodeType(n1,n2) {
            return n1.type === n2.type && n1.key === n2.key
        }

        // 左端对比
        while(i <= e1 && i <= e2) {
            let n1 = c1[i]
            let n2 = c2[i]
            if(isSomeVNodeType(n1,n2)) {
                patch(n1,n2,container,parentComponent,parentAnchor)
            } else {
                break;
            }
            i++;
        }

        // 右端对比
        while(i <= e1 && i <= e2) {
            let n1 = c1[e1]
            let n2 = c2[e2]
            if(isSomeVNodeType(n1,n2)) {
                patch(n1,n2,container,parentComponent,parentAnchor)
            } else {
                break;
            }
            e1--;
            e2--;
        }

        // 新的比老的长 创建
        if(i > e1) {
            if(i <= e2) {
                // 确定锚点
                const nextPos = e2 + 1
                const anchor = nextPos < c2.length ? c2[nextPos].el : null 
                while(i <= e2) {
                    patch(null,c2[i],container,parentComponent,anchor)
                    i++;
                }    
            }
        } else if (i > e2) { 
            // 老的比新的长 删除
            while(i <= e1) {
                hostRemove(c1[i].el)
                i++;
            }
        } else {
            // 解决中间乱序
            let s1 = i
            let s2 = i
            const toBePatched = e2 - s2 + 1  // newChildren乱序的长度
            let patched = 0
            const keyToNewIndexMap = new Map()
            const newIndexToOldIndexMap = new Array(toBePatched) // 获取固定的数组
            let moved = false
            let maxIndexSoFar = 0

            for (let i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0 // 初始化映射表newIndexToOldIndexMap

            // 将乱序的新节点都存储在映射表里
            for (let i = s2; i <= e2; i++) {
                const nextChild = c2[i]
                keyToNewIndexMap.set(nextChild.key,i)
            }
            console.log('MAP',keyToNewIndexMap);

            for(let i = s1;i <= e1;i++) {
                let prevChild = c1[i]
                if(patched >= toBePatched) {
                    hostRemove(prevChild.el)
                    continue;
                }   
                let newIndex
                // 这是当老节点有key的情况下
                if(prevChild.key != null) {
                    console.log('旧节点',prevChild.key);
                    newIndex = keyToNewIndexMap.get(prevChild.key)
                } else {
                    for(let j = s2;j <= e2;j++) {
                        if(isSomeVNodeType(prevChild,c2[j])) {
                            newIndex = j
                            break;
                        }
                    } 
                }  
                if(newIndex === undefined) {
                    hostRemove(prevChild.el)
                } else {
                    if(newIndex >= maxIndexSoFar) {
                        maxIndexSoFar = newIndex
                    } else {
                        moved= true
                    }
                    newIndexToOldIndexMap[newIndex - s2] = i + 1
                    patch(prevChild,c2[newIndex],container,parentComponent,null)
                    patched++
                }
            }

            const increasingNewIndexSequence = moved ? getSequence(newIndexToOldIndexMap) : []
            console.log('increasingNewIndexSequence',increasingNewIndexSequence);

            let j = increasingNewIndexSequence.length -1
            for (let i = toBePatched -1 ; i >= 0 ; i--) {
                const nextIndex = i + s2
                const nextChild = c2[nextIndex]
                const anchor = nextIndex + 1 < c2.length  ? c2[nextIndex + 1].el : null

                if(newIndexToOldIndexMap[i] === 0 ) {
                    // 创建
                    patch(null,nextChild,container,parentComponent,anchor)
                } else if(moved) {
                    if( j < 0 || i !== increasingNewIndexSequence[j]){
                        console.log('移动位置');
                        hostInsert(nextChild.el,container,anchor)
                    } else {
                        j--;
                    }
                }
            }            
        }
    }

    function unmountChildren(children) {
        hostRemove(children)
    }

    function mountElement(vnode: any, container: any,parentComponent,anchor) {

        const el = (vnode.el = hostCreateElement(vnode.type))

        let { children,shapeFlag } = vnode

        // children -> string || array
        if(shapeFlag & shapeFlags.TEXT_CHILDREN) {
            el.textContent = children
        } else if(shapeFlag & shapeFlags.ARRAY_CHILDREN) {
            mountChildren(vnode.children,el,parentComponent,anchor)
        }

        const { props } = vnode
        if(props) {
            for (let key in props) {
                const val = props[key]
                hostPatchProp(el,key,null,val)
            }
        }

        // container.append(el)
        hostInsert(el,container,anchor)
    }

    function mountChildren(children,container,parentComponent,anchor) {
        children.forEach(v => {
            patch(null,v,container,parentComponent,anchor)
        })
    }

    function processComponent(n1,n2: any,container:any,parentComponent,anchor) {
        if(!n1) {
            mountComponent(n2,container,parentComponent,anchor)
        } else {
            updateComponent(n1,n2)
        }
    }

    function updateComponent(n1,n2) {
        const instance = (n2.component = n1.component)
        if(shouldUpdateComponent(n1,n2)) {
            instance.next = n2
            instance.update()
        } else {
            n2.el = n1.el
            instance.vnode = n2
        }
    }

    // 处理组件
    function mountComponent(initialVNode: any,container:any,parentComponent,anchor) {
        const instance = (initialVNode.component =  createComponentInstance(initialVNode,parentComponent))
        // 这个方法，就是调用setup，然后给component身上挂载render方法
        setupComponent(instance)
        // 此时instance也就是component上已经有render方法了，直接调用就可以拿到subTree也就是虚拟树
        // 这个时候再次调用patch方法，虚拟树里边就是element元素了
        setupRenderEffect(instance,initialVNode,container,anchor)
    }

    function setupRenderEffect(instance:any,initialVNode,container:any,anchor) {
        instance.update = effect(() => {
            if(!instance.isMounted) {
                console.log('init');
                const { proxy } = instance; 
                const subTree = (instance.subTree = instance.render.call(proxy,proxy))
                patch(null,subTree,container,instance,anchor)
                initialVNode.el = subTree.el
                instance.isMounted = true
            } else {
                console.log('异步一次更新');
                // 需要一个vnode
                const { next,vnode } = instance
                if(next) {
                    next.el = vnode.el
                    updateComponentPreRender(instance,next)
                }
                const { proxy } = instance; 
                const subTree = instance.render.call(proxy,proxy)
                const prevSubTree = instance.subTree
                instance.subTree = subTree
                patch(prevSubTree,subTree,container,instance,anchor)
            }
        },{
            scheduler() {
                console.log('update-scheduler');
                // 为其传入fn的返回值，也就是effect的执行函数
                queueJobs(instance.update)
            }
        })
    }

    return {
        createApp:createAppAPI(render)
    }
}


function updateComponentPreRender(instance,nextVNode) {
    instance.vnode = nextVNode
    instance.next = null
    instance.props = nextVNode.props
}

// 求最长递增子序列算法
function getSequence(arr: number[]): number[] {
    const p = arr.slice();
    const result = [0];
    let i, j, u, v, c;
    const len = arr.length;
    for (i = 0; i < len; i++) {
      const arrI = arr[i];
      if (arrI !== 0) {
        j = result[result.length - 1];
        if (arr[j] < arrI) {
          p[i] = j;
          result.push(i);
          continue;
        }
        u = 0;
        v = result.length - 1;
        while (u < v) {
          c = (u + v) >> 1;
          if (arr[result[c]] < arrI) {
            u = c + 1;
          } else {
            v = c;
          }
        }
        if (arrI < arr[result[u]]) {
          if (u > 0) {
            p[i] = result[u - 1];
          }
          result[u] = i;
        }
      }
    }
    u = result.length;
    v = result[u - 1];
    while (u-- > 0) {
      result[u] = v;
      v = p[v];
    }
    return result;
}
  